#include <NewSoftSerial.h>
/* All LCD functions from http://forum.sparkfun.com/viewtopic.php?f=8&t=20829 */

NewSoftSerial lcdSerialPort(8, 9);

const int formSwitch = 2;
const int backSwitch = 3;
boolean formState;
boolean backState;

short temperature;
long pressure;
int humidityA;
int windA;
int rainACC;
int curSpeed, lCurSpeed;
double tempC, lTempC;
double tempF, lTempF;
double pressInch, lPressInch;
double humPerc, lHumPerc;
double totalRainIn = 0;
double windDV;
int deg;
int i = 0, j = 0;

byte buf[14];

void setup() {
  
  pinMode(formSwitch, INPUT);
  pinMode(backSwitch, INPUT);
  digitalWrite(formSwitch,HIGH);
  digitalWrite(backSwitch, HIGH);
  attachInterrupt(0, unitInt, CHANGE);
  attachInterrupt(1, backInt, CHANGE);

  backState = false;
  formState = digitalRead(formSwitch);
  
  Serial1.begin(9600);
  Serial.begin(9600);
  
  delay(2000);
  
  setBaudRate(9600);

  clearScreen();
  setBackgroundBrightness(99);
  writeTemplate();
}


void loop() {
  if(backState) {
    backState = false;
    toggleReverse();
    writeTemplate();
  }
  temperature = 0;
  pressure = 0;
  humidityA = 0;
  windA = 0;
  rainACC = 0;
  curSpeed = 0;
  
  if(Serial1.available() > 0) {
    while((char)Serial1.read() != '~');
    buf[0] = Serial1.read();
    buf[1] = Serial1.read();
    temperature = ((unsigned short)buf[0]) << 8 | ((unsigned short)buf[1]);
    buf[2] = Serial1.read();
    buf[3] = Serial1.read();
    buf[4] = Serial1.read();
    buf[5] = Serial1.read();
    pressure = ((unsigned long)buf[2]) << 24 | ((unsigned long)buf[3]) << 16 | ((unsigned long)buf[4]) << 8 | ((unsigned long)buf[5]);
    buf[6] = Serial1.read();
    buf[7] = Serial1.read();
    humidityA = ((unsigned int)buf[6]) << 8 | ((unsigned int)buf[7]);
    buf[8] = Serial1.read();
    buf[9] = Serial1.read();
    windA = ((unsigned int)buf[8]) << 8 | ((unsigned int)buf[9]);
    buf[10] = Serial1.read();
    buf[11] = Serial1.read();
    rainACC = ((unsigned int)buf[10]) << 8 | ((unsigned int)buf[11]);
    buf[12] = Serial1.read();
    buf[13] = Serial1.read();
    curSpeed = ((unsigned int)buf[12]) << 8 | ((unsigned int)buf[13]);
  }
  
  tempC = ((double) temperature) / 10.0;
  tempF = (tempC * (9.0/5.0)) + 32;
  pressInch = ((double)pressure) / 3386.389;
  humPerc = (((double)humidityA / 1023.0) - 0.16) / 0.0062;
  humPerc = humPerc/ (1.0546 - 0.00216*tempC);
  if(rainACC < 10 && rainACC >= 0) {
    totalRainIn += (rainACC / 3) * 0.011;
  }
  windDV = (((double)windA) / 1023.0) * 5;
  
  if(windDV < 3.86 && windDV > 3.82) {
    deg = 0;
  }else if (windDV > 1.96 && windDV < 2.00) {
    deg = 22;
  }else if (windDV > 2.23 && windDV < 2.27) {
    deg = 45;
  }else if (windDV > 0.39 && windDV < 0.43) {
    deg = 67;
  }else if (windDV > 0.43 && windDV < 0.47) {
    deg = 90;
  }else if (windDV > 0.30 && windDV < 0.34) {
    deg = 112;
  }else if (windDV > 0.88 && windDV < 0.92) {
    deg = 135;
  }else if (windDV > 0.60 && windDV < 0.64) {
    deg = 157;
  }else if (windDV > 1.38 && windDV < 1.42) {
    deg = 180;
  }else if (windDV > 1.17 && windDV < 1.21) {
    deg = 202;
  }else if (windDV > 3.06 && windDV < 3.1) {
    deg = 225;
  }else if (windDV > 2.91 && windDV < 2.95) {
    deg = 247;
  }else if (windDV > 4.6 && windDV < 4.64) {
    deg = 270;
  }else if (windDV > 4.02 && windDV < 4.06) {
    deg = 292;
  }else if (windDV > 4.76 && windDV < 4.8) {
    deg = 315;
  }else if (windDV > 3.41 && windDV < 3.45) {
    deg = 337;
  }
  if(j==0){
    lTempF = tempF;
    lTempC = tempC;
    lPressInch = pressInch;
    lHumPerc = humPerc;
    lCurSpeed = curSpeed;
  }
  else{
    if(tempF - lTempF > 30 || tempF - lTempF < -30) {
      tempF = lTempF;
    }
    if(tempC - lTempC > 30 || tempC - lTempC < -30) {
      tempC = lTempC;
    }
    if(lPressInch - pressInch > 30 || lPressInch - pressInch < -30) {
      pressInch = lPressInch;
    }
    if(humPerc > 100 || humPerc < 0) {
      humPerc = lHumPerc;
    }
    if(lCurSpeed - curSpeed > 45 || lCurSpeed - curSpeed < -45) {
      curSpeed = lCurSpeed;
    }
  }
    
  lTempF = tempF;
  lTempC = tempC;
  lPressInch = pressInch;
  lHumPerc = humPerc;
  lCurSpeed = curSpeed;
  Serial.println(tempF);
  Serial.println(pressInch);
  Serial.println(humPerc);
  Serial.println(curSpeed);
  Serial.println(deg);
  Serial.println(totalRainIn);

  
 
 if(formState) {
  setX(36);
  setY(62);
  lcdSerialPort.print(tempF);
  print(" deg F");
  setX(61);
  setY(52);
  lcdSerialPort.print(pressInch);
  print(" inHg");
  setX(61);
  setY(42);
  lcdSerialPort.print(humPerc);
  print("%");
  setX(36);
  setY(32);
  lcdSerialPort.print(curSpeed);
  print(" mph @ ");
  lcdSerialPort.print(deg);
  setX(36);
  setY(22);
  lcdSerialPort.print(totalRainIn);
  print(" inches"); 
 }
 else{
  setX(36);
  setY(62);
  lcdSerialPort.print(tempC);
  print(" deg C");
  setX(61);
  setY(52);
  lcdSerialPort.print(pressInch * 25.4);
  print(" mmHg");
  setX(61);
  setY(42);
  lcdSerialPort.print(humPerc);
  print("%");
  setX(36);
  setY(32);
  lcdSerialPort.print(curSpeed * 1.6);
  print(" kph @ ");
  lcdSerialPort.print(deg);
  setX(36);
  setY(22);
  lcdSerialPort.print(totalRainIn * 2.54);
  print(" cm"); 
 }
  
  i++;
  if(i==10) {
    writeTemplate();
    i = 0;
  }
  delay(1000);
  if(j==0){
    j=1;
  }
}

//Display functions

void writeTemplate() {
    clearScreen();
    setX(1);
    setY(62);
    print("Temp: ");
    setX(1);
    setY(53);
    print("Pressure: ");
    setX(1);
    setY(43);
    print("Humidity: ");
    setX(1);
    setY(33);
    print("Wind: ");
    setX(1);
    setY(23);
    print("Rain: "); 
}

void toggleReverse() {
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x12,BYTE);
}

void print(char *data){
  lcdSerialPort.print(data);
}

void clearScreen(){
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x00,BYTE);
}

void demo(){
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x04,BYTE);
}

void toggleSplashScreen(){
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x13,BYTE);
}

void setBackgroundBrightness(byte value){
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x02,BYTE);
  lcdSerialPort.print(value,BYTE);
}

void setBaudRate(long value){
  // Get the internal reference for this baud rate
  char *ref = " ";
  if(value == 4800)
    ref = "1";
  else if(value == 9600)
    ref = "2";
  else if(value == 19200)
    ref = "3";
  else if(value == 38400)
    ref = "4";
  else if(value == 57600)
    ref = "5";
  else if(value == 115200)
    ref = "6";
  else
    return;

  // Since it often rolls back to 115200, try setting it via 115200 first
  lcdSerialPort.begin(115200);
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x07,BYTE);
  lcdSerialPort.print(ref);

  // Now change the serial port to the desired rate, and set it again.
  lcdSerialPort.begin(value);
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x07,BYTE);
  lcdSerialPort.print(ref);
}

void setX(byte value){
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x18,BYTE);
  lcdSerialPort.print(value,BYTE);
}

void setY(byte value){
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x19,BYTE);
  lcdSerialPort.print(value,BYTE);
}

void setPixel(byte state){
  lcdSerialPort.print(0x50,BYTE);
  lcdSerialPort.print(0x40,BYTE);
  lcdSerialPort.print(state,BYTE);
}

void drawLine(byte startX, byte startY, byte endX, byte endY, byte state){
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x0C,BYTE);
  lcdSerialPort.print(startX,BYTE);
  lcdSerialPort.print(startY,BYTE);
  lcdSerialPort.print(endX,BYTE);
  lcdSerialPort.print(endY,BYTE);
  lcdSerialPort.print(state,BYTE);
}

void drawCircle(byte startX, byte startY, byte radius, byte state){
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x03,BYTE);
  lcdSerialPort.print(startX,BYTE);
  lcdSerialPort.print(startY,BYTE);
  lcdSerialPort.print(radius,BYTE);
  lcdSerialPort.print(state,BYTE);
}

void drawBox(byte topLeftX, byte topLeftY, byte bottomRightX, byte bottomRightY, byte state){
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x0F,BYTE);
  lcdSerialPort.print(topLeftX,BYTE);
  lcdSerialPort.print(topLeftY,BYTE);
  lcdSerialPort.print(bottomRightX,BYTE);
  lcdSerialPort.print(bottomRightY,BYTE);
  lcdSerialPort.print(state,BYTE);
}

void eraseBox(byte topLeftX, byte topLeftY, byte bottomRightX, byte bottomRightY, byte state){
  lcdSerialPort.print(0x7C,BYTE);
  lcdSerialPort.print(0x05,BYTE);
  lcdSerialPort.print(topLeftX,BYTE);
  lcdSerialPort.print(topLeftY,BYTE);
  lcdSerialPort.print(bottomRightX,BYTE);
  lcdSerialPort.print(bottomRightY,BYTE);
  lcdSerialPort.print(state,BYTE);
}

void backInt() {
  static long lastBack;
  if(millis() - lastBack  > 100) {
    backState = !backState;
    lastBack = millis();
  }
}

void unitInt() {
  static long lastUnit;
  if(millis() - lastUnit > 100) {
    formState = digitalRead(formSwitch);
    lastUnit = millis();
  }
}
